#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>

/**
 * 支持自动重连的客户端
 */

using namespace std;

enum ExitCode
{
  USAGE_ERR = 1,
  SOCKET_ERR
};

enum class Status
{
  NEW,
  CONNECTING,
  CONNECTED,
  DISCONNECTED,
  CLOSED
};

static const int defaultsockfd = -1;
static const int defaultretryinterval = 1;
static const int defaultmaxretries = 5;

class ClientConnection
{
public:
  ClientConnection(const std::string &serverip, uint16_t serverport)
      : _sockfd(), _serverip(serverip), _serverport(serverport), _status(Status::NEW), _retry_interval(defaultretryinterval), _max_retries(defaultmaxretries)
  {
  }
  Status ConnectionStatus()
  {
    return _status;
  }
  void Connect()
  {
    _sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (_sockfd < 0)
    {
      cerr << "socket create error" << endl;
      exit(ExitCode::SOCKET_ERR);
    }
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(_serverport);
    inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr); // 将点分十进制风格的ip转换为四字节并放到第三个参数的地址上
    int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
    if (n < 0)
    {
      // 1.关闭sockfd
      Disconnect();
      // 2.更新状态
      _status = Status::DISCONNECTED;
      return;
    }
    _status = Status::CONNECTED;
    cout << "connect success" << endl;
  }
  void Reconnect()
  {
    int cnt = 0;
    while (true)
    {
      _status = Status::CONNECTING;
      Connect();
      if (_status == Status::CONNECTED)
      {
        break;
      }
      cnt++;
      if (cnt > _max_retries)
      {
        _status = Status::CLOSED;
        cerr << "Reconnect error" << endl;
        break;
      }
      cout << "Reconnecting... cnt : " << cnt << endl;
      sleep(_retry_interval);
    }
  }
  void Process() // 正常的IO通信
  {
    while (true)
    {
      string message = "hello server";
      ssize_t n = send(_sockfd, message.c_str(), message.size(), 0);
      if (n > 0)
      {
        char buffer[1024];
        ssize_t m = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
        if (m > 0)
        {
          buffer[m] = 0;
          cout << "server echo# " << buffer << endl;
        }
        else
        {
          _status = Status::DISCONNECTED;
          break;
        }
      }
      else
      {
        cerr << "send error" << endl;
        _status = Status::CLOSED;
        break;
      }
      sleep(1);
    }
  }
  void Disconnect()
  {
    if (_sockfd > defaultsockfd)
    {
      close(_sockfd);
      _sockfd = defaultsockfd;
      _status = Status::CLOSED;
    }
  }
  ~ClientConnection() {}

private:
  int _sockfd;
  std::string _serverip;
  uint16_t _serverport;
  Status _status;      // 客户端状态
  int _retry_interval; // 重连时间间隔
  int _max_retries;    // 最大重连次数
};

class TcpClient
{
public:
  TcpClient(const std::string &serverip, uint16_t serverport)
      : _connection(serverip, serverport)
  {
  }
  void Excute()
  {
    while (true)
    {
      switch (_connection.ConnectionStatus())
      {
      case Status::NEW:
        _connection.Connect();
        break;
      case Status::CONNECTED:
        _connection.Process();
        break;
      case Status::DISCONNECTED:
        _connection.Reconnect();
        break;
      case Status::CLOSED:
        _connection.Disconnect();
        return;
      default:
        // do noting
        break;
      }
    }
  }
  ~TcpClient() {}

private:
  ClientConnection _connection;
};

void Usage(std::string process)
{
  std::cout << "Usage : " << process << "serverip serverport" << std::endl;
}

// tcp_client serverip serverport
int main(int argc, char *argv[])
{
  if (argc != 3)
  {
    Usage(argv[0]);
    exit(ExitCode::USAGE_ERR);
  }
  std::string serverip = argv[1];
  uint16_t serverport = std::stoi(argv[2]);
  TcpClient tcpclient(serverip, serverport);
  tcpclient.Excute();
  return 0;
}